// ============================================================================
//
// Copyright (C) 2006-2007 Dengues
//
// Google Group: http://groups.google.com/group/dengues
// QQ Group: 24885404
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
// ============================================================================
package org.dengues.designer.core.data;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.dengues.core.DenguesCorePlugin;
import org.dengues.core.components.EGlobalComponentNames;
import org.dengues.core.components.ENodeParameterName;
import org.dengues.core.components.IComponentsFactory;
import org.dengues.core.components.IXMLComponent;
import org.dengues.core.components.NodePropertyParameter;
import org.dengues.core.external.AbstractCompNodeExt;
import org.dengues.core.external.IExternalData;
import org.dengues.core.metadata.MetadataTable;
import org.dengues.core.process.AbstractCompNode;
import org.dengues.core.process.CompElement;
import org.dengues.core.process.EConnectionType;
import org.dengues.core.process.ICompBlock;
import org.dengues.core.process.ICompBlockExit;
import org.dengues.core.process.ICompConnection;
import org.dengues.core.process.ICompGefNode;
import org.dengues.core.process.ICompNode;
import org.dengues.core.process.ICompProcess;
import org.dengues.core.process.ICompSubProcess;
import org.dengues.core.process.IGefNode;
import org.dengues.core.property.EParameterFieldCategory;
import org.dengues.core.property.EPropertyCategory;

/**
 * Qiang.Zhang.Adolf@gmail.com class global comment. Detailled comment <br/>
 * 
 * $Id: Dengues.epf Qiang.Zhang.Adolf@gmail.com 2008-1-15 qiang.zhang $
 * 
 */
public class CompDataProcess {

    private ICompProcess compProcess;

    private final List<ICompNode> dataNodes;

    private final List<ICompNode> checkedNodes;

    private final Map<ICompNode, AbstractCompNode> nodesMap;

    /**
     * Qiang.Zhang.Adolf@gmail.com CompDataProcess constructor comment.
     * 
     * @param compProcess
     */
    public CompDataProcess(ICompProcess compProcess) {
        this();
        this.compProcess = compProcess;
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "buildDataNodes".
     */
    public void buildDataNodes() {
        for (ICompNode compNode : compProcess.getCompNodes()) {
            buildDataNode(compNode);
        }
        for (ICompNode compNode : compProcess.getCompNodes()) {
            if (compNode.isSubProcessStart()) {
                checkDataLink(compNode);
            }
        }

        for (ICompNode compNode : compProcess.getCompNodes()) {
            List<? extends ICompConnection> outgoingConns = compNode.getOutgoingConnections();
            for (ICompConnection connection : outgoingConns) {
                if (EConnectionType.TRG_OK.equals(connection.getType())) {
                    buildDataNode(compNode).getTrgOkUniques().add(connection.getTarget().getUniqueName());
                    AbstractCompNode dataTarget = buildDataNode((ICompNode) connection.getTarget());
                    NodePropertyParameter parameter = new NodePropertyParameter();
                    parameter.setPropertyCategory(EPropertyCategory.MAIN);
                    parameter.setFieldCategory(EParameterFieldCategory.CHECK);
                    parameter.setName(ENodeParameterName.PROCESS_MAIN_ROOT_NODE.getName());
                    parameter.setShow(false);
                    parameter.setValue("true");
                    dataTarget.getElementParameters().add(parameter);
                }
            }
        }
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "checkDataLink".
     * 
     * @param compNode
     */
    private void checkDataLink(ICompNode compNode) {
        if (checkedNodes.contains(compNode)) {
            return;
        }
        checkedNodes.add(compNode);
        for (ICompConnection connection : compNode.getOutgoingConnections()) {
            if (EConnectionType.DATA.equals(connection.getType())) {
                ICompNode rootTarget = ((ICompGefNode) compNode).getSubProcessStartNode(false);
                ICompNode rootSource = ((ICompGefNode) connection.getTarget()).getSubProcessStartNode(true);

                AbstractCompNode dataSource = nodesMap.get(rootSource);
                AbstractCompNode dataTarget = nodesMap.get(rootTarget);

                NodePropertyParameter parameter = new NodePropertyParameter();
                parameter.setPropertyCategory(EPropertyCategory.MAIN);
                parameter.setFieldCategory(EParameterFieldCategory.CHECK);
                parameter.setName(ENodeParameterName.PROCESS_MAIN_ROOT_NODE.getName());
                parameter.setShow(false);
                parameter.setValue("true");
                dataTarget.getElementParameters().add(parameter);

                dataSource.getDataUniques().add(rootTarget.getUniqueName());
                CompDataConnection dataConnec;
                MetadataTable clone = connection.getMetadata().clone();

                IComponentsFactory componentsFactory = DenguesCorePlugin.getDefault().getDesignerCoreService()
                        .getComponentsFactory();
                String name = EGlobalComponentNames.ZDATAHASH.getName();
                IXMLComponent component = componentsFactory.getComponent(name);
                CompDataNode dataNode = new CompDataNode(component, name + connection.getName());
                dataNode.setStart(false);
                dataNode.getMetadataList().clear();
                MetadataTable clone2 = clone.clone();
                clone2.setTableName(dataNode.getUniqueName());
                dataNode.getMetadataList().add(clone2);
                dataNode.setSubProcessStart(false);

                dataSource = dataTarget;
                dataTarget = dataNode;
                dataConnec = new CompDataConnection();
                dataConnec.setType(EConnectionType.MAIN);
                dataConnec.setSource(dataSource);
                dataConnec.setTarget(dataTarget);
                dataConnec.setName(connection.getName());
                clone = connection.getMetadata().clone();
                clone.setTableName(dataConnec.getName());
                dataConnec.setMetadata(clone);
                dataSource.addOutgoingConnections(dataConnec);
                dataTarget.addIncomingConnections(dataConnec);
            }
        }
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com CompDataProcess constructor comment.
     */
    public CompDataProcess() {
        dataNodes = new ArrayList<ICompNode>();
        nodesMap = new HashMap<ICompNode, AbstractCompNode>();
        checkedNodes = new ArrayList<ICompNode>();
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "buildDataNode".
     * 
     * @param graphicalNode
     * @return
     */
    public AbstractCompNode buildDataNode(ICompNode graphicalNode) {
        if (nodesMap.containsKey(graphicalNode)) {
            return nodesMap.get(graphicalNode);
        }
        AbstractCompNode dataNode = new CompDataNode();
        dataNodes.add(dataNode);
        nodesMap.put(graphicalNode, dataNode);

        dataNode.setStart(graphicalNode.isStart());
        List<MetadataTable> metadataList = graphicalNode.getMetadataList();
        List<MetadataTable> newmetaList = new ArrayList<MetadataTable>();
        for (MetadataTable metadataTable : metadataList) {
            newmetaList.add(metadataTable.clone());
        }
        dataNode.setMetadataList(newmetaList);
        dataNode.setElementParameters(graphicalNode.getComponent().createNodeParameters(dataNode));
        initializeDataFromGraphical(dataNode, graphicalNode);
        initializeExternalNode(dataNode, graphicalNode);
        ICompBlock compBlock = ((CompElement) graphicalNode).getParent();
        String uniqueName = graphicalNode.getUniqueName();
        if (compBlock != null) {
            uniqueName = compBlock.getBlockId() + uniqueName;
        }
        dataNode.setUniqueName(uniqueName);
        dataNode.setSubProcessStart(graphicalNode.isSubProcessStart());
        dataNode.setCompProcess(graphicalNode.getCompProcess());
        dataNode.setComponent(graphicalNode.getComponent());
        CompDataConnection dataConnec;
        for (ICompConnection connection : graphicalNode.getOutgoingConnections()) {
            if (EConnectionType.MAIN.equals(connection.getType())) {
                dataConnec = new CompDataConnection();
                dataConnec.setMetadata(connection.getMetadata());
                compBlock = connection.getParent();
                uniqueName = connection.getName();
                if (compBlock != null) {
                    uniqueName = compBlock.getBlockId() + uniqueName;
                }
                dataConnec.setName(uniqueName);
                dataConnec.setSource(dataNode);
                dataConnec.setType(connection.getType());
                dataNode.addOutgoingConnections(dataConnec);
                IGefNode target2 = connection.getTarget();
                IGefNode bNode = target2;
                if (target2 instanceof ICompBlock) {
                    ICompBlock target22 = (ICompBlock) target2;
                    bNode = target22.getRootNode();
                }
                if (bNode == null || bNode instanceof ICompBlockExit) {
                    if (bNode instanceof ICompBlockExit) {
                        ICompBlockExit blockExit = (ICompBlockExit) bNode;
                        target2 = (IGefNode) blockExit.getParent();
                        bNode = null;
                    }
                    List<? extends ICompConnection> otConns = target2.getOutgoingConnections();
                    if (!otConns.isEmpty()) {
                        bNode = otConns.get(0).getTarget();
                    }
                }
                if (bNode != null) {
                    ICompNode target = buildDataNode((ICompNode) bNode);
                    dataConnec.setTarget(target);
                    target.addIncomingConnections(dataConnec);
                }
            }
        }
        return dataNode;
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "initializeExternalNode".
     * 
     * @param dataNode
     * @param graphicalNode
     */
    private void initializeExternalNode(AbstractCompNode dataNode, ICompNode graphicalNode) {
        AbstractCompNodeExt externalNode = ((ICompGefNode) graphicalNode).getExternalNode(false);
        if (externalNode != null) {
            IExternalData externalData = externalNode.getExternalData();
            dataNode.setExternalData(externalData.clone());
        }
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "initializeDataFromGraphical".
     * 
     * @param newNode
     * @param graphicalNode
     */
    private void initializeDataFromGraphical(ICompNode newNode, ICompNode graphicalNode) {
        for (NodePropertyParameter curParam : graphicalNode.getElementParameters()) {
            NodePropertyParameter dataNodeParam = newNode.getElementParameter(curParam.getName());
            if (dataNodeParam != null) {
                dataNodeParam.setValue(curParam.getValue());
                if (dataNodeParam.getFieldCategory() == EParameterFieldCategory.TABLE) {
                    dataNodeParam.setItemValues(curParam.getItemValues());
                    List<Map<String, Object>> value = (List<Map<String, Object>>) dataNodeParam.getValue();
                    List<Map<String, Object>> newvalue = new ArrayList<Map<String, Object>>();
                    for (Map<String, Object> map : value) {
                        Map<String, Object> newmap = new HashMap<String, Object>();
                        newmap.putAll(map);
                        newvalue.add(newmap);
                    }
                    dataNodeParam.setValue(newvalue);
                }
            }
        }
    }

    /**
     * Getter for dataNodes.
     * 
     * @return the dataNodes
     */
    public List<ICompNode> getDataNodes() {
        return this.dataNodes;
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "buildCompSubProcesses".
     * 
     * @return
     */
    public List<? extends ICompSubProcess> buildCompSubProcesses() {
        List<ICompSubProcess> compSubProcesses = new ArrayList<ICompSubProcess>();
        for (ICompNode node : getDataNodes()) {
            if (node.isSubProcessStart()) {
                compSubProcesses.add(new CompSubProcess(node));
            }
        }
        return compSubProcesses;
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "getRootCompNodes".
     * 
     * @return
     */
    public List<ICompNode> getRootCompNodes() {
        List<ICompNode> list = new ArrayList<ICompNode>();
        for (ICompNode compNode : getDataNodes()) {
            if (compNode.isSubProcessStart()) {
                list.add(compNode);
            }
        }
        return list;
    }

}
